<?php
 /**
  * Clase HTML
  * Requiere LIGA 2.0 o superior
  * Autor: Ing. Oscar Galileo García García
  * Creación: 17 de Abril del 2011
  * Última modificación: 27 de Noviembre del 2011
  */
 class HTML {
    private function __construct() {
        // Sólo se usarán los métodos así: HTML::tabla($liga);
    }
    // Obtiene un texto mejorado para su presentación como título de columna
    private static function mejorar($cad) {
        $cad = preg_replace('/([a-z])([A-Z])/','$1 $2',$cad);
        // Parche debido a que las funciones siguientes rompen la codificación de caracteres
        return utf8_encode(ucwords(strtolower(str_replace('_',' ',utf8_decode($cad)))));
    }
    // Obtiene el código necesario para ser ejecutado y saber si para una columna dada el registro cumple cierta condición (propiedades extras condicionadas)
    private static function prop_cond($obj,$col,$prop) {
        $resp = '';
        if(!is_array($prop)) {
            return $resp;
        }
        foreach($prop as $colCond => $pro) {
            if(strpos($colCond,$col)===0 && strpos($colCond,'@si(')!==false) {
                $ini = strpos($colCond,'@si(')+3;
                $cond = substr($colCond,$ini);
                if($cond!='()') {
                    $pro = str_replace("'",'"',$pro);
                    $resp .= " @{if($cond)return '$pro';else return '';}@";
                }
            }
        }
        return $resp;
    }
    // Convierte un objeto LIGA a un arreglo asociativo (usando sólo las dos primeras columnas) ideal para las entidades y optgroup
    private static function LIGA2arreglo($obj) {
        if($obj->numReg()<1 || $obj->numCol()<2) {
            return array();
        }
        $info = $obj->info();
        $resp = array();
        foreach($info['info'] as $idx => $reg) {
            $col1 = current($reg);
            $col2 = next($reg);
            $resp[$col1] = $col2;
        }
        return $resp;
    }
    // Intercambia la información del objeto LIGA ingresado por los valores del arreglo asociativo que coinciden
    private static function entidades($obj,$col,$arreglo) {
        $datos = $obj->columna($col);
        foreach($datos as $idx => $dato) {
            if(array_key_exists($dato,$arreglo)) {
                $obj->cambiar($idx,$col,$arreglo[$dato]);
            }
        }
        return $obj;
    }
    // Validar la ejecución de las funciones del objeto HTML
    private static function validar($liga,$regs=true) {
        if(get_class($liga)!='LIGA') {
            throw new Error('El objeto proporcionado no es una LIGA (No se puede estirar xP)');
        }
        if($liga->numCol()==0) {
            throw new Error('El objeto LIGA proporcionado no tiene columnas válidas');
        }
        if($liga->numReg()==0 && $regs) {
            return '<div>No se encontraron registros</div>';
        }
        return false;
    }
    // Obtiene las columnas expandiendo * por todas
    private static function todos($liga,$cols) {
        if(in_array('*',$cols)) {
            $pri = array();
            $seg = array();
            $otro = true;
            foreach($cols as $k => $val) {
                if($val==='*' && $otro) {
                    unset($cols[$k]);
                    $otro = false;
                    continue;
                }
                if($otro) {
                    $pri[$k] = $val;
                } else {
                    $seg[$k] = $val;
                }
            }
            $todas = array_keys($liga->meta());
            $cols = array_merge($pri,$todas,$seg);
            $cols = self::todos($liga,$cols);
        }
        return $cols;
    }
    // Obtiene una tabla HTML a partir del objeto LIGA dado y parámetros opcionales para personalizar
    public static function tabla($liga,$tit=false,$cols=false,$prop=false,$ent=false,$pie=false) {
        if($resp = self::validar($liga)) {
            return $resp;
        }
        ob_start();
        $pro = isset($prop['table']) ? htmlentities($liga->vars(0,' '.$prop['table']),ENT_NOQUOTES,'UTF-8') : '';
        echo "<table$pro>";
        // Si se especificó un título personalizado
        if(is_string($tit) && $tit!='') {
            $pro = isset($prop['caption']) ? htmlentities($liga->vars(0,' '.$prop['caption']),ENT_NOQUOTES,'UTF-8') : '';
            $tit = htmlentities($tit,ENT_NOQUOTES,'UTF-8');
            echo "<caption$pro>$tit</caption>";
        }
        // Si ingresó el parámetro de entidades se intercambian los valores en la tabla antes de mostrar la información
        if(is_array($ent) && count($ent)>0) {
            foreach($ent as $col => $vals) {
                if($liga->col_existe($col)) {
                    if(!is_array($vals) && is_object($vals) && get_class($vals)=='LIGA') {
                        $vals = self::LIGA2arreglo($vals);
                    }
                    self::entidades($liga,$col,$vals);
                }
            }
        }
        // Si ingresó el parámetro del $pie de tabla se guarda en una variable y se imprime entre el thead y el tbody
        if(is_string($pie) && $pie!='') {
            $pie = $liga->vars(0,$pie);
            $pro = isset($prop['tfoot']) ? htmlentities($liga->vars(0,' '.$prop['tfoot']),ENT_NOQUOTES,'UTF-8') : '';
            $pie = "<tfoot$pro><tr>".$liga->ejec($pie).'</tr></tfoot>';
        }
        // La forma más básica sin columnas seleccionadas
        if(is_bool($cols) || $cols==='*') {
            $ths = '';
            $tds = '';
            foreach($liga->meta() as $col => $pro) {
                $pro = isset($prop["th$col"]) ? htmlentities($liga->vars(0,' '.$prop["th$col"]),ENT_NOQUOTES,'UTF-8') : '';
                $ths .= "<th$pro>".self::mejorar($col).'</th>';
                $pro = isset($prop[$col]) ? htmlentities(' '.$prop[$col],ENT_NOQUOTES,'UTF-8') : '';
                $pro .= self::prop_cond($liga,$col,$prop);
                $tds .= "<td$pro>@col[$col]</td>";
            }
        } else {
            $cols = is_string($cols) ? explode(',',$cols) : $cols;
            // Recorro el arreglo para cambiar * por todas las columnas
            $cols = self::todos($liga,$cols);
            if(is_array($cols) && count($cols)>0) {
                $ths = '';
                $tds = '';
                foreach($cols as $k => $val) {
                    $col = is_string($k) ? $k : $val;
                    $pro = isset($prop["th$col"]) ? htmlentities($liga->vars(0,' '.$prop["th$col"]),ENT_NOQUOTES,'UTF-8') : '';
                    $ths .= "<th$pro>".self::mejorar($col).'</th>';
                    $pro = isset($prop[$col]) ? htmlentities(' '.$prop[$col],ENT_NOQUOTES,'UTF-8') : '';
                    $pro .= self::prop_cond($liga,$col,$prop);
                    if(is_string($k) && is_string($val) && $val!='') {
                        $tds .= "<td$pro>$val</td>";
                    } else {
                        $tds .= "<td$pro>@col[$col]</td>";
                    }
                }
            } else {
                throw new Error("El parámetro para las columnas no es válido ($cols)");
            }
        }
        $pro = isset($prop['thead']) ? htmlentities($liga->vars(0,' '.$prop['thead']),ENT_NOQUOTES,'UTF-8') : '';
        echo "<thead$pro><tr>$ths</tr></thead>";
        echo $pie;
        $pro = isset($prop['tbody']) ? htmlentities($liga->vars(0,' '.$prop['tbody']),ENT_NOQUOTES,'UTF-8') : '';
        echo "<tbody$pro>";
        $pro = isset($prop['tr']) ? htmlentities(' '.$prop['tr'],ENT_NOQUOTES,'UTF-8') : '';
        $liga->mostrarRegistros("<tr$pro>$tds</tr>");
        echo '</tbody>';
        echo '</table>';
        return ob_get_clean();
    }
    // Obtiene una lista (ol o ul) HTML de elementos con la segunda columna de la tabla, considerando la primera como llave primaria
    public static function lista($liga,$ord=false,$prop=false,$comp=true) {
        if($resp = self::validar($liga)) {
            return $resp;
        }
        ob_start();
        $tipo = $ord ? 'ol' : 'ul';
        if($comp) {
            $pro = is_array($prop) && array_key_exists($tipo,$prop) ? $liga->ejec($liga->vars(0,htmlentities(' '.$prop[$tipo],ENT_NOQUOTES,'UTF-8'))) : '';
            echo "<$tipo$pro>";
        }
        $pro = is_array($prop) && array_key_exists('li',$prop) ? htmlentities(' '.$prop['li'],ENT_NOQUOTES,'UTF-8') : '';
        $pro .= self::prop_cond($liga,'li',$prop);
        $col = is_array($prop) && array_key_exists('li(cont)',$prop) && $prop['li(cont)']!='' ? $prop['li(cont)'] : '';
        if($col=='') {
            $cols = array_keys($liga->meta());
            $col = '@col['.next($cols).']';
        }
        $cad = "<li$pro>$col</li>";
        $liga->mostrarRegistros($cad);
        if($comp) {
            echo "</$tipo>";
        }
        return ob_get_clean();
    }
    // Obtiene un formulario HTML válido y listo para usarse en peticiones asíncronas o de manera clásica
    public static function forma($liga,$tit=false,$cols=false,$prop=false,$comp=true,$vals=array()) {
        if($resp = self::validar($liga,false)) {
            return $resp;
        }
        ob_start();
        if($comp) {
            $pro = is_array($prop) && array_key_exists('form',$prop) ? $liga->ejec($liga->vars(0,htmlentities(' '.$prop['form'],ENT_NOQUOTES,'UTF-8'))) : '';
            echo "<form$pro>";
            $pro = is_array($prop) && array_key_exists('fieldset',$prop) ? $liga->ejec($liga->vars(0,htmlentities(' '.$prop['fieldset'],ENT_NOQUOTES,'UTF-8'))) : '';
            echo "<fieldset$pro>";
        }
        // Si ingresó un título para el formulario
        if(is_string($tit) && $tit!='') {
            $tit = htmlentities($tit,ENT_NOQUOTES,'UTF-8');
            $pro = is_array($prop) && array_key_exists('legend',$prop) ? $liga->ejec($liga->vars(0,htmlentities(' '.$prop['legend'],ENT_NOQUOTES,'UTF-8'))) : '';
            echo "<legend$pro>$tit</legend>";
        }
        $campos = '';
        $cols = is_array($cols) && count($cols)>0 ? $cols : (is_string($cols) ? explode(',',$cols) : array_keys($liga->meta()));
        $cols = self::todos($liga,$cols);
        if(is_array($cols) && count($cols)>0) {
            foreach($cols as $k => $val) {
                $col = is_string($k) ? $k : $val;
                $prodiv = is_array($prop) && array_key_exists('div',$prop) ? $liga->ejec($liga->vars(0,htmlentities(' '.$prop['div'],ENT_NOQUOTES,'UTF-8'))) : '';
                $prolabel = is_array($prop) && array_key_exists('label',$prop) ? $liga->ejec($liga->vars(0,htmlentities(' '.$prop['label'],ENT_NOQUOTES,'UTF-8'))) : '';
                $proinput = is_array($prop) && array_key_exists('input',$prop) ? $liga->ejec($liga->vars(0,htmlentities(' '.$prop['input'],ENT_NOQUOTES,'UTF-8'))) : '';
                if(is_string($k) && is_string($val) && $val!='') {
                    // Algo personalizado
                    $val = $liga->ejec($liga->vars(0,$val));
                    $val = strpos($val,'<')===false ? "<label for='$col'$prolabel>$val</label> <input id='$col' name='$col'$proinput />" : $val;
                    $val = strpos($val,'label for=')===false ? "<label for='$col'$prolabel>".self::mejorar($col)."&nbsp;</label> $val" : $val;
                    $campos .= "<div$prodiv>$val</div>";
                } else {
                    // Nada personalizado, si la columna existe tomará los datos de $liga
                    if($liga->col_existe($col)) {
                        // La columna existe! (armar a partir de las propiedades del campo en $liga)
                        $campo = '';
                        // Prefijo para los id y que no se repitan en el mismo sistema ;D
                        $pref = is_array($prop) && array_key_exists('prefid',$prop) ? $liga->ejec($liga->vars(0,htmlentities($prop['prefid'],ENT_NOQUOTES,'UTF-8'))) : '';
                        $idname = "id='$pref$col' name='$col'";
                        if(!$liga->col_prop($col,'ai')) {
                            // No es autoincrementable, ahora checar null, blob, com y si hay algún value
                            $req = $liga->col_prop($col,'null') ? '' : ' *';
                            $fecha = strpos($liga->col_prop($col,'tipo'),'date')===false ? '' : " class='fecha'";
                            $fecha = strpos($liga->col_prop($col,'tipo'),'stamp')===false ? $fecha : " class='fecha'";
                            $max = $liga->col_prop($col,'max') ? " maxlength='".$liga->col_prop($col,'max')."'" : '';
                            $com = $liga->col_prop($col,'com') ? " title='".$liga->col_prop($col,'com')."'" : '';
                            $val = isset($vals[$col]) && is_string($vals[$col]) ? ' value="'.$liga->ejec($liga->vars(0,htmlentities($vals[$col],ENT_NOQUOTES,'UTF-8'))).'"' : '';
                            $tipo = $liga->col_prop($col,'blob') ? "<textarea $idname$com$proinput$val></textarea>" : "<input $idname$fecha$max$com$proinput$val />";
                            // Soporte para llaves foráneas...
                            $tipo = $liga->col_prop($col,'referencia') ? HTML::selector(new LIGA(substr($liga->col_prop($col,'referencia'),0,strpos($liga->col_prop($col,'referencia'),'::'))),array('select'=>"$idname$fecha$max$com$proinput$val",'option'=>'value="@col['.substr($liga->col_prop($col,'referencia'),strpos($liga->col_prop($col,'referencia'),'::')+2).']"')) : $tipo;
                            $etiq = "<label for='$pref$col'$prolabel>".self::mejorar($col)."$req&nbsp;</label>";
                            $campo = "<div$prodiv>$etiq $tipo</div>";
                        }
                        $campos .= $campo;
                    } else {
                        // Insertar algo muy genérico
                        $campos .= "<div$prodiv><label for='$col'$prolabel>".self::mejorar($col)."</label> <input id='$col' name='$col'$proinput /></div>";
                    }
                }
            }
        }
        echo $campos;
        $enviar = is_array($prop) && array_key_exists('submit',$prop) && is_string($prop['submit']) && $prop['submit']!='' ? $prop['submit'] : "<input type='submit' value='Enviar' />";
        $cancel = is_array($prop) && array_key_exists('reset',$prop) && is_string($prop['reset']) && $prop['reset']!='' ? $prop['reset'] : "<input type='reset' value='Cancelar' />";
        $pro = is_array($prop) && array_key_exists('btns',$prop) ? $liga->ejec($liga->vars(0,htmlentities(' '.$prop['btns'],ENT_NOQUOTES,'UTF-8'))) : '';
        echo "<div$pro>$enviar $cancel</div>";
        if($comp) {
            echo "</fieldset>";
            echo "</form>";
        }
        return ob_get_clean();
    }
    // Obtiene un selector HTML con la segunda columna de la tabla en el contenido, considerando la primera como llave primaria
    public static function selector($liga,$prop=false,$optgro=false,$comp=true,$car=0) {
        if($resp = self::validar($liga)) {
            return $resp;
        }
        ob_start();
        if($comp) {
            $pro = is_array($prop) && array_key_exists('select',$prop) ? $liga->ejec($liga->vars(0,htmlentities(' '.$prop['select'],ENT_NOQUOTES,'UTF-8'))) : '';
            echo "<select$pro>";
        }
        $pro = is_array($prop) && array_key_exists('option',$prop) ? htmlentities(' '.$prop['option'],ENT_NOQUOTES,'UTF-8') : '';
        $pro .= self::prop_cond($liga,'option',$prop);
        // Para modificar el contenido de los options
        $col = is_array($prop) && array_key_exists('option(cont)',$prop) && $prop['option(cont)']!='' ? $prop['option(cont)'] : '';
        if($col=='') {
            $cols = array_keys($liga->meta());
            $col = '@col['.next($cols).']';
            $col = is_integer($car) && $car>0 ? "@{if($car<strlen('$col')-1)return substr('$col',0,$car+1).'...';else return '$col';}" : $col;
        }
        // Lógica para agrupar los options
        $llaves = is_array($optgro) ? array_keys($optgro) : array();
        $llave = current($llaves); # sólo interesa la primera
        if(is_array($optgro) && count($optgro)>0 && $liga->col_existe($llave)) {
            $grupos = array_values($optgro);
            $grupos = current($grupos);
            if(!is_array($grupos) && is_object($grupos) && get_class($grupos)=='LIGA') {
                $grupos = self::LIGA2arreglo($grupos);
            }
            foreach($grupos as $k => $val) {
                $val = htmlentities($val,ENT_QUOTES,'UTF-8');
                echo "<optgroup label='$val'>";
                $info = $liga->info();
                foreach($info['info'] as $idx => $dat) {
                    if($liga->cond($idx,"@col[$llave]=='$k'")) {
                        echo $liga->ejec($liga->vars($idx,"<option$pro>$col</option>"));
                    }
                }
                echo '</optgroup>';
            }
        } else { # Forma básica sin agrupación
            $cad = "<option$pro>$col</option>";
            $liga->mostrarRegistros($cad);
        }
        if($comp) {
            echo "</select>";
        }
        return ob_get_clean();
    }
    // Obtiene una serie de botones radio a partir de un objeto LIGA o arreglo
    public static function radio($obj,$nom,$derecha=false) {
        if(!is_array($obj) && get_class($obj)=='LIGA') {
            $obj = self::LIGA2arreglo($obj);
        }
        if(!is_string($nom) || $nom=='') {
            throw new Error("Debe proporcionar un nombre válido");
        }
        ob_start();
        foreach($obj as $k => $v) {
            if(!is_string($k) && !is_string($v)) {
                throw new Error("Los elementos del arreglo no son válidos");
            }
            $k = htmlentities($k,ENT_QUOTES,'UTF-8');
            $v = htmlentities($v,ENT_QUOTES,'UTF-8');
            if($derecha) {
                echo "<label><input type='radio' name='$nom' value='$k'> $v</label>";
            } else {
                echo "<label>$v <input type='radio' name='$nom' value='$k'></label>";
            }
        }
        return ob_get_clean();
    }
    // Obtiene una serie de checkbox a partir de un objeto LIGA o arreglo
    public static function checkbox($obj,$nom,$derecha=false) {
        if(!is_array($obj) && get_class($obj)=='LIGA') {
            $obj = self::LIGA2arreglo($obj);
        }
        if(!is_string($nom) || $nom=='') {
            throw new Error("Debe proporcionar un nombre válido");
        }
        ob_start();
        foreach($obj as $k => $v) {
            if(!is_string($k) && !is_string($v)) {
                throw new Error("Los elementos del arreglo no son válidos");
            }
            $k = htmlentities($k,ENT_QUOTES,'UTF-8');
            $v = htmlentities($v,ENT_QUOTES,'UTF-8');
            if($derecha) {
                echo "<label><input type='checkbox' name='$nom' value='$k'> $v</label>";
            } else {
                echo "<label>$v <input type='checkbox' name='$nom' value='$k'></label>";
            }
        }
        return ob_get_clean();
    }
    // Alias de forma
    public static function formulario($liga,$tit=false,$cols=false,$prop=false,$comp=true) {
        return self::forma($liga,$tit,$cols,$prop,$comp);
    }
    // Alias de forma
    public static function form($liga,$tit=false,$cols=false,$prop=false,$comp=true) {
        return self::forma($liga,$tit,$cols,$prop,$comp);
    }
    // Alias de selector
    public static function select($liga,$prop=false,$optgroup=false,$comp=true,$car=0) {
        return self::selector($liga,$prop,$optgroup,$comp,$car);
    }
    // Alias de lista
    public static function ul($liga,$prop=false) {
        return self::lista($liga,false,$prop,true);
    }
    // Alias de lista pero numerada por defecto
    public static function ol($liga,$prop=false) {
        return self::lista($liga,true,$prop,true);
    }
    // Alias de tabla
    public static function table($liga,$tit=false,$cols=false,$prop=false,$ent=false,$foot=false) {
        return self::tabla($liga,$tit,$cols,$prop,$ent,$foot);
    }
 }
?>